// ======================================================================
// Copyright (C) 2013 Hell-Prototypes. / www.hellprototypes.com
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at
// your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// ======================================================================
`timescale 10ns/1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    16:29:34 05/28/2012 
// Design Name: 
// Module Name:    USB 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
//Three always blocks code style - registered outputs
module USB(
		input      CLK_USB, nRST,
		output     [1:0] USB_FIFOADDR,
		output     nUSB_PKTEND, 
		output reg nUSB_SLWR, nUSB_SLOE, nUSB_SLRD,
		input      nUSB_FULL,
		input      nUSB_EMPTY,
		inout [7:0] USB_FD,
		
		input [15:0] FIFO_DOUT,
		output reg  FIFO_RD_EN,
		input	    FIFO_EMPTY,

		output reg execute,
		output     [39:0] command
    );

parameter 
	IDLE	= 3'b000,
	S1		= 3'b001,
	S2		= 3'b010,
	S3		= 3'b011,
	S4		= 3'b100,
	S5		= 3'b101,
	S6		= 3'b110,
	S7		= 3'b111;

parameter
    READADDR   = 1'b0,
    WRITEADDR  = 1'b1;
//=============================================================================
reg [2:0] state, next_state;
reg [7:0] cmd, next_cmd;
reg [31:0] cmd_param, next_cmd_param;
reg usb_fifo_addr_1, next_usb_fifo_addr_1;
//reg next_nUSB_PKTEND;
reg next_nUSB_SLWR;
reg next_nUSB_SLOE;
reg next_nUSB_SLRD;
reg next_execute = 0;
reg [7:0] usb_fd_reg, next_usb_fd_reg;
reg [7:0] usb_fd_reg_buff, next_usb_fd_reg_buff;
reg usb_full_flag, next_usb_full_flag;
reg next_FIFO_RD_EN;

//=============================================================================
assign command = {cmd, cmd_param};
assign USB_FIFOADDR[0] = 0;
assign USB_FIFOADDR[1] = usb_fifo_addr_1;
assign USB_FD = nUSB_SLOE ? usb_fd_reg : 8'bzzzzzzzz;
assign nUSB_PKTEND = 1;
//=============================================================================
always @(posedge CLK_USB or negedge nRST) begin
	if(!nRST) begin
		state <= IDLE;
		usb_fifo_addr_1 <= READADDR;
		//nUSB_PKTEND <= 1;
		nUSB_SLWR <= 1;
		nUSB_SLOE <= 1;
		nUSB_SLRD <= 1;
		usb_fd_reg <= 8'h00;
		usb_fd_reg_buff <= 8'h00;
		usb_full_flag <= 0;
		FIFO_RD_EN <= 0;
		execute <= 0;
		cmd <= 8'h00;
		cmd_param <= 32'h00000000;
	end else begin
		state <= next_state;
		usb_fifo_addr_1 <= next_usb_fifo_addr_1;
		//nUSB_PKTEND <= next_nUSB_PKTEND;
		nUSB_SLWR <= next_nUSB_SLWR;
		nUSB_SLOE <= next_nUSB_SLOE;
		nUSB_SLRD <= next_nUSB_SLRD;
		usb_fd_reg <= next_usb_fd_reg;
		usb_fd_reg_buff <= next_usb_fd_reg_buff;
		usb_full_flag <= next_usb_full_flag;
		FIFO_RD_EN <= next_FIFO_RD_EN;
		execute <= next_execute;
		cmd <= next_cmd;
		cmd_param <= next_cmd_param;
	end
end

always @(*) begin
	next_state = state;
	next_usb_fifo_addr_1 = usb_fifo_addr_1;
	next_nUSB_SLOE = nUSB_SLOE;
	next_nUSB_SLRD = 1;
	next_execute = 0;
	next_cmd = cmd;
	next_cmd_param = cmd_param;

	next_FIFO_RD_EN = 0;
	next_usb_fd_reg = usb_fd_reg;
	next_usb_fd_reg_buff = usb_fd_reg_buff;
	next_usb_full_flag = usb_full_flag;
	//next_nUSB_PKTEND = 1;
	next_nUSB_SLWR = nUSB_SLWR;

	
	case (state)
		IDLE: begin
			if(nUSB_EMPTY) begin //Read data from usb
				next_usb_fifo_addr_1 = READADDR;
				next_nUSB_SLOE = 0;
				next_state  = S1;
			end else begin
				next_nUSB_SLOE = 1;
				if(nUSB_FULL && (!FIFO_EMPTY)) begin //Write data to usb
					if(!usb_full_flag) begin
						next_FIFO_RD_EN = 1;
					end
					next_usb_fifo_addr_1 = WRITEADDR;
					next_state  = S4;
				end
			end
		end
		S1:	begin
			next_nUSB_SLRD = 0;
			next_cmd = USB_FD;
			next_state = S2;
		end
		S2: begin
			next_state = S3;
		end
		S3: begin
			if(nUSB_EMPTY) begin
				next_nUSB_SLRD = 0;
				next_cmd_param = {cmd_param[23:0],USB_FD};
				next_state  = S2;
			end else begin
				next_execute = 1;
				next_state  = IDLE;
			end
		end
		
		//write to usb
		S4: begin
			next_usb_fd_reg =  FIFO_DOUT[7:0];
			next_usb_fd_reg_buff = FIFO_DOUT[15:8];
			if(FIFO_EMPTY || nUSB_EMPTY) begin
				//next_nUSB_PKTEND = 0; //This will be make usb breakdown
				next_nUSB_SLWR = 1;
				next_usb_full_flag = 0;
				next_state = IDLE;
			end else if(!nUSB_FULL) begin
				next_usb_full_flag = 1;
				next_nUSB_SLWR = 1;
				next_state = IDLE;
			end else begin
				next_nUSB_SLWR = 0;
				next_state = S5;
			end
		end
		S5: begin
			next_FIFO_RD_EN = 1;
			next_usb_fd_reg = usb_fd_reg_buff;
			next_state = S4;
		end
	endcase
end 

endmodule
